package de.unibi.comet.tools;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import de.unibi.comet.fa.CDFA;
import de.unibi.comet.fa.DFAFactory;
import de.unibi.comet.fa.GeneralizedString;
import de.unibi.comet.fa.NodeLimitExceededException;
import de.unibi.comet.fa.PrositeMotifParser;
import de.unibi.comet.util.Log;

public class prosite {

	public static void usage() {
		System.out.println("usage: prosite <database> <protein>");
		System.out.println("   <database> file containing patterns");
		System.out.println("   <protein> string to test matching on");
		System.exit(1);
	}

	public static int naiveMatch(String s, List<GeneralizedString> pl) {
		Log.getInstance().startTimer();
		int matches=0;
		for (int i=0; i<s.length(); ++i) {
			for (GeneralizedString pattern : pl) {
				if (pattern.matches(s, i)) {
					matches+=1;
					// Log.getInstance().printNormal(String.format("m %d (%s)", i+pattern.length()-1, s.substring(i, i+pattern.length())));
					// Log.getInstance().printNormal(String.format("m %d", i+pattern.length()-1));
					// break;
				}
			}
		}
		Log.getInstance().stopTimer("naive matching");
		return matches;
	}

	public static void main(String[] args) {
		if (args.length!=2) usage();
		
		FileInputStream patternFile = null;
		try {
			patternFile = new FileInputStream(args[0]);
		} catch (FileNotFoundException e) {
			System.out.println("File not found, sorry!");
			System.exit(1);
		}
		BufferedReader br = new BufferedReader(new InputStreamReader(patternFile));
		ArrayList<String> patterns = new ArrayList<String>();
		try {
			while (true) {
				String line = br.readLine();
				if (line==null) break;
				patterns.add(line);
				// System.out.printf("line: %s\n",line);
			}
		} catch (IOException e) {
			System.out.println("I/O failure, sorry!");
			System.exit(1);
		}

		String protein = null;
		FileInputStream genomeFile = null;
		try {
			genomeFile = new FileInputStream(args[1]);
		} catch (FileNotFoundException e) {
			System.out.println("File not found, sorry!");
			System.exit(1);
		}
		br = new BufferedReader(new InputStreamReader(genomeFile));
		try {
			String line = br.readLine();
			if (line==null) {
				System.out.println("Couldn't read protein");
				System.exit(1);
			}
			protein=line;
			System.out.println(String.format("Read protein: %d characters", protein.length()));
		} catch (IOException e) {
			System.out.println("I/O failure, sorry!");
			System.exit(1);
		}

		
		Log.getInstance().setTimingActive(true);
		Log.getInstance().setLogLevel(Log.Level.INSANE);
		// Log.getInstance().setLogLevel(Log.Level.EVERYTHING);
		
		// ---
//		Character[] chars = {'A','B'};
//		Alphabet alphabet = new Alphabet(Arrays.asList(chars));
//		List<GeneralizedString> l_ = new LinkedList<GeneralizedString>();
//		l_.add(new GeneralizedString(alphabet, "AB?B"));
//		CDFA cdfa_ = DFAFactory.build(l_.get(0).getAlphabet(), l_);
//		System.out.println(cdfa_.toString());
//		cdfa_.minimizeHopcroft();
//		System.exit(1);
		// ---
		
		int n=0;
		for (String s : patterns) {
			List<GeneralizedString> l = null;
			System.out.println(String.format("pattern:\"%s\"",s));
			try {
				l = PrositeMotifParser.parse(s);
				// System.out.println(l.size());
			} catch (IllegalArgumentException e) {
				System.out.println(String.format("invalid :\"%s\"",s));
				continue;
			}
			
			int nodeLimit = 2000000/l.size();
			
			float timeSubset = -1.0f;
			int states = -1; 
			int countNaive = -1;
			int countDfa = -1;
			int statesHopcroft = -1;
			float timeHopcroft = -1.0f;
			int countDfaHopcroft = -1;
			// int countDfaKozen = -1;
			int statesKozen = -1;
			float timeKozen = -1.0f;
			int statesWithNonOnePeriodicity = -1;
			
			try {
				CDFA cdfa = DFAFactory.build(l.get(0).getAlphabet(), l, nodeLimit);
				timeSubset = Log.getInstance().getLastPeriodCpu();
				states = cdfa.getStateCount();
				System.out.println(String.format("dfa states: \"%d\"", states));
	
				countNaive = naiveMatch(protein, l);
				System.out.println(String.format("Count naive: %d", countNaive));
				
				countDfa = cdfa.countMatchesDFA(protein);
				System.out.println(String.format("Count DFA: %d", countDfa));
				// System.out.println(cdfa.toString());
				
				CDFA cdfaHopcroft = cdfa.minimizeHopcroft();
				statesHopcroft = cdfaHopcroft.getStateCount();
				timeHopcroft = Log.getInstance().getLastPeriodCpu();
				
				if (statesHopcroft<=500) {
					System.out.println(cdfaHopcroft.toString());
					// cdfa.analysePerodicity();
					statesWithNonOnePeriodicity = cdfaHopcroft.analysePeriodicity();
				}
				
				countDfaHopcroft = cdfaHopcroft.countMatchesDFA(protein);
				System.out.println(String.format("Count DFA (hopcroft): %d", countDfaHopcroft));
				if (countDfaHopcroft!=countNaive) {
					System.out.println(">! BUG: countNaive!=countHopcroft");
				}
			
				if (states<500) {
					CDFA cdfaKozen = cdfa.minimizeKozen();
					timeKozen = Log.getInstance().getLastPeriodCpu();
					statesKozen = cdfaKozen.getStateCount();
					// System.out.println(cdfa.toString());
					// countDfaKozen = cdfaKozen.countMatchesDFA(protein);
					// System.out.println(String.format("Count DFA (kozen): %d", countDfaKozen));
					if (statesKozen!=statesHopcroft) {
						System.out.println(">! BUG: statesKozen!=statesHopcroft");
					}
				}
			} catch (NodeLimitExceededException e) {
				System.out.println(String.format(">! node-limit of %d exceeded", nodeLimit));
			}
			System.out.println(String.format("> %d %e %d %d %e %d %e %d %d %d %s", l.size(), timeSubset, states, statesHopcroft, timeHopcroft, statesKozen, timeKozen, countNaive, countDfaHopcroft, statesWithNonOnePeriodicity, s));
			
			// if (n++==0) System.exit(1);
		}
		
	}
}
